package ph.com.gs3.formalistics.services.managers;

import ph.com.gs3.formalistics.global.constants.LoggingType;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.RemoteDataAccessObject.RDAOCommunicationException;
import ph.com.gs3.formalistics.model.facades.UsersDataReader;
import ph.com.gs3.formalistics.model.vo.business.Company;
import ph.com.gs3.formalistics.model.vo.business.User;
import ph.com.gs3.formalistics.services.managers.SessionManager.LoginException;
import ph.com.gs3.formalistics.services.synchronizers.CommentsSynchronizer;
import ph.com.gs3.formalistics.services.synchronizers.DocumentsSynchronizer;
import ph.com.gs3.formalistics.services.synchronizers.DocumentsSynchronizer.DocumentsSynchronizerEventListener;
import ph.com.gs3.formalistics.services.synchronizers.FormsSynchronizer;
import ph.com.gs3.formalistics.services.synchronizers.OutgoingActionsSynchronizer;
import ph.com.gs3.formalistics.services.synchronizers.exceptions.SynchronizationFailedException;
import ph.com.gs3.formalistics.services.synchronizers.exceptions.SynchronizationPrematureException;
import android.content.Context;

public class DataSynchronizationManager {

	public static final String TAG = DataSynchronizationManager.class.getSimpleName();

	public static LoggingType LOGGING_TYPE;

	private boolean isFullUpdateOngoing;
	private Context context;

	public static enum UpdateMode {
		NO_REAUTHENTICATION, REAUTHENTICATE_FIRST
	}

	public static enum SyncType {
		FULL_SYNC, SEND_ACTIONS, REFRESH_DOCUMENTS
	};

	private SessionManager sessionMan;

	private DocumentsSynchronizerEventListener documentsSynchronizerEventListener;
	private FullUpdateManagerEventsListener eventsListener;

	// ==========================================================================
	// {{ Instance Keeper

	private DataSynchronizationManager(Context context) {
		this.isFullUpdateOngoing = false;
		this.context = context;
		this.sessionMan = SessionManager.getApplicationInstance();
	}

	private static DataSynchronizationManager instance;

	public static DataSynchronizationManager createApplicationInstance(Context context) {

		if (instance == null) {
			instance = new DataSynchronizationManager(context);
		}

		return instance;

	}

	public static DataSynchronizationManager getApplicationInstance() {
		return instance;
	}

	// }}

	/**
	 * Full update will first check if the session manager is authenticated. If it's not,
	 * then this will try to execute login using the User object passed to this method.
	 * 
	 * If the session manager is now authenticated, full synchronization will start,
	 * otherwise, the events listener will be notified that re-authentication is needed.
	 * 
	 * @param user
	 *            The authenticated user or user that will be used to re-authenticated
	 *            before starting the synchronization.
	 */
	public void startFullUpdate(SyncType syncType) {

		boolean isAuthenticated = false;

		UsersDataReader dataReader = new UsersDataReader(context);

		User activeUser = dataReader.getActiveUser();
		User authenticatedUser = null;

		if (sessionMan == null) {
			throw new RuntimeException("Session manager cannot be null");
		}

		if (activeUser == null) {
			log("Sych suspended, no active user yet");
			isAuthenticated = false;
		} else {
			// Re-authenticate if the session manager is not authenticated yet.
			if (!sessionMan.isAuthenticated()) {
				log("Reauthenticating @" + activeUser.getCompany().getServer());
				try {
					authenticatedUser = reauthenticate(activeUser);
					isAuthenticated = true;
				} catch (LoginException e) {
					log("Failed automatic login - " + e.getCause().getMessage());
					if (e.getCause() instanceof RDAOCommunicationException) {
						// Login failed due to connection, stop the synchronization
						// service, but do not require re-authentication

						log("Sych suspended due to connection problems");

						eventsListener.onFullUpdateDone();
						return;
					} else {
						isAuthenticated = false;
					}
				}
			} else {
				// The active user is already authenticated
				isAuthenticated = sessionMan.isAuthenticated();
				authenticatedUser = activeUser;
			}
		}

		// Execute full update if authenticated, otherwise, notify that the application
		// needs an authenticated user
		if (isAuthenticated) {
			startSynchronization(authenticatedUser, syncType);
			eventsListener.onFullUpdateDone();
		} else {
			eventsListener.onReauthenticationNeeded();
		}

	}

	private User reauthenticate(User user) throws LoginException {

		Company userCompany = user.getCompany();

		String server = userCompany.getServer();
		String email = user.getEmail();
		String password = user.getPassword();

		return sessionMan.login(server, email, password);

	}

	/**
	 * Executes all synchronization tasks in order.
	 * 
	 * @param user
	 *            The user that owns the records to be synchronized.
	 */
	private void startSynchronization(User user, SyncType syncType) {

		// Avoid multiple calls to full synchronization
		if (!isFullUpdateOngoing) {
			isFullUpdateOngoing = true;

			if (syncType == SyncType.FULL_SYNC) {

				// Synchronize the forms
				FormsSynchronizer formsSynchronizer = new FormsSynchronizer(context, user);
				try {
					log("Task 1: Synchronizing forms");
					formsSynchronizer.synchronize();
					eventsListener.onFormsSynchronized();
				} catch (SynchronizationFailedException e) {
					// TODO: notify the user about synchronization failure
					log("Task 1 (Failed): " + e.getMessage());
				}
			}

			// Synchronize the outgoing actions
			OutgoingActionsSynchronizer outgoingActionsSynchronizer = new OutgoingActionsSynchronizer(
			        context, user);

			try {
				outgoingActionsSynchronizer.synchronize();
				eventsListener.onOutgoingActionsSent();
			} catch (SynchronizationPrematureException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// Synchronize the documents
			DocumentsSynchronizer documentsSynchronizer = new DocumentsSynchronizer(context, user,
			        documentsSynchronizerEventListener);
			try {
				log("Task 3: Synchronizing documents");
				documentsSynchronizer.synchronize();
				eventsListener.onDocumentsSychronized();
			} catch (SynchronizationPrematureException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (syncType == SyncType.FULL_SYNC) {
				log("Task 4: Synchronizing comments");
				CommentsSynchronizer commentsSynchronizer = new CommentsSynchronizer(context, user);
				commentsSynchronizer.synchronize();
			}

			isFullUpdateOngoing = false;
		}
	}

	private void log(String log) {

		if (LOGGING_TYPE == LoggingType.ENABLED) {
			FLLogger.d(TAG, log);
		}

	}

	// }

	// ==========================================================================
	// {{ Events Listener Management Methods & Interface

	public FullUpdateManagerEventsListener getEventsListener() {
		return eventsListener;
	}

	public void setEventsListener(FullUpdateManagerEventsListener eventsListener) {
		this.eventsListener = eventsListener;
	}

	public void setDocumentsSynchronizerEventListener(
	        DocumentsSynchronizerEventListener documentsSynchronizerEventListener) {
		this.documentsSynchronizerEventListener = documentsSynchronizerEventListener;
	}

	public static interface FullUpdateManagerEventsListener {

		public void onFormsSynchronized();

		public void onReauthenticationNeeded();

		public void onFullUpdateDone();

		public void onOutgoingActionsSent();

		public void onDocumentsSychronized();

	}

	// }}

}
